package org.babyfish.jimmer.sql.kt.model.logic.instant

import org.babyfish.jimmer.meta.ImmutableType
import org.babyfish.jimmer.sql.kt.common.AbstractQueryTest
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.expect
import java.time.Instant
import kotlin.math.abs

/**
 * Integration test for Instant type support with KSP processing
 * This test verifies that KSP correctly processes Kotlin entities with Instant logical deletion
 */
class InstantKspIntegrationTest : AbstractQueryTest() {

    @Test
    fun testKspGeneratesCorrectMetadata() {
        // Verify that KSP generates the correct ImmutableType metadata
        val type = ImmutableType.get(EKt::class.java)
        assertNotNull(type, "ImmutableType should be generated by KSP")
        
        val deletedTimeProp = type.getProp("deletedTime")
        assertNotNull(deletedTimeProp, "deletedTime property should exist")
        expect(Instant::class.java) { deletedTimeProp.returnClass }
        expect(true) { deletedTimeProp.isNullable }
        
        val logicalDeletedInfo = type.logicalDeletedInfo
        assertNotNull(logicalDeletedInfo, "LogicalDeletedInfo should be generated")
        expect(deletedTimeProp) { logicalDeletedInfo.prop }
    }

    @Test
    fun testKspGeneratesCorrectDraftClasses() {
        // Test that KSP generates working Draft classes
        val entity = EKt {
            id = 100L
            deletedTime = null
        }
        
        expect(100L) { entity.id }
        expect(null) { entity.deletedTime }
        
        // Test modifying the entity
        val modifiedEntity = EKt(entity) {
            deletedTime = Instant.now()
        }
        
        expect(100L) { modifiedEntity.id }
        assertNotNull(modifiedEntity.deletedTime, "deletedTime should be set")
    }

    @Test
    fun testInstantNowValueGeneration() {
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null")
        
        // Test generateValue() for "now" annotation
        val generatedValue = info.generateValue()
        assertTrue(generatedValue is Instant, "Generated value should be an Instant for @LogicalDeleted(\"now\")")
        
        val instant = generatedValue as Instant
        val now = Instant.now()
        
        // Verify the generated value is close to current time
        val timeDiff = abs(instant.toEpochMilli() - now.toEpochMilli())
        assertTrue(timeDiff < 1000, "Generated Instant should be within 1 second of current time. Diff: ${timeDiff}ms")
    }

    @Test
    fun testInstantNullValueGeneration() {
        val type = ImmutableType.get(FKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null")
        
        // Test generateValue() for "null" annotation
        val generatedValue = info.generateValue()
        expect(null) { generatedValue }
    }

    @Test
    fun testLogicalDeletionBehavior() {
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null")
        
        // Test isDeleted behavior
        val now = Instant.now()
        
        // For nullable fields with "now" value:
        // - null means "not deleted" (active state)
        // - non-null means "deleted" (logical deletion state)
        expect(false) { info.isDeleted(null) }
        expect(true) { info.isDeleted(now) }
    }

    @Test
    fun testKotlinEntityLogicalDeletion() {
        // Test Kotlin entity logical deletion functionality
        val kotlinType = ImmutableType.get(EKt::class.java)
        val kotlinInfo = kotlinType.logicalDeletedInfo

        assertNotNull(kotlinInfo, "Kotlin entity should have LogicalDeletedInfo")

        // Should generate Instant values for "now" logical deletion
        val kotlinValue = kotlinInfo.generateValue()
        assertTrue(kotlinValue is Instant, "Kotlin entity should generate Instant")

        // Should have correct property type
        expect(Instant::class.java) { kotlinInfo.prop.returnClass }
    }

    @Test
    fun testEntityCreationAndModification() {
        // Test creating entities with different deletion states
        val activeEntity = EKt {
            id = 1L
            deletedTime = null  // Not deleted (active)
        }

        val deletedEntity = EKt {
            id = 2L
            deletedTime = Instant.now()  // Deleted (logically deleted)
        }

        expect(1L) { activeEntity.id }
        expect(null) { activeEntity.deletedTime }

        expect(2L) { deletedEntity.id }
        assertNotNull(deletedEntity.deletedTime, "deletedTime should be set")

        // Test logical deletion state
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo!!

        expect(false) { info.isDeleted(activeEntity.deletedTime) }  // null = not deleted
        expect(true) { info.isDeleted(deletedEntity.deletedTime) }  // non-null = deleted
    }

    @Test
    fun testNullLogicalDeletionEntity() {
        // Test FKt entity with @LogicalDeleted("null")
        val entity = FKt {
            id = 3L
            deletedTime = null
        }
        
        expect(3L) { entity.id }
        expect(null) { entity.deletedTime }
        
        val type = ImmutableType.get(FKt::class.java)
        val info = type.logicalDeletedInfo!!
        
        // For "null" logical deletion, the behavior is different
        expect(null) { info.generateValue() }
    }
}
